/**
*	Class Name 	: CertUtil.java
*	Description	: 인증서 관련 Util 클래스
*	Modification Information
*	
*	     수정일		 수정자				수정내용
*	-----------	-----	-------------------------------
*	2011. 8. 8.	이주영      최초작성
*
*	Copyright 2011 (주)이타기술.  All right reserved.
*/

package gov.mogaha.ntis.cmm.mob.servlet;

import gov.mogaha.ntis.cmm.mob.servlet.bean.Cert;
import gov.mogaha.ntis.cmm.mob.servlet.exception.NotCertificateException;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.dreamsecurity.magicxsign.MagicXSign;
import com.dreamsecurity.magicxsign.MagicXSign_Exception;
import com.dreamsecurity.magicxsign.MagicXSign_Type;
import com.dreamsecurity.mobile.MagicMRSPhone.MagicMRSPhone;

public class CertUtil {
	
	/** 인증서 서버 IP */
	private static final String 	SERVER_IP = "152.99.58.200";
	/** 인증서 서버 포트 */
	private static final int 		SERVER_PORT = 10001;
	/** 인증서 서버 ID */
	private static final String 	SERVER_ID = "GPKIAPP";
	
	public static final int GET_AUTHCODE_START = 1000;
	public static final int GET_AUTHCODE_OK = 1001;	
	public static final int SET_HOST_INFO_FAIL = 1002;
	public static final int SET_USER_INFO_FAIL = 1003;
	public static final int GET_AUTHCODE_FAIL = 1004;
	public static final int GET_USAGE_FAIL = 1005;
	public static final int COPY_CERT_OK = 2001;
	public static final int COPY_CERT_END = 2002;
	public static final int COPY_CERT_ERROR = 2003;
	
	private static final int INDEX = 0;	
	private static final String SDCARD = Environment.getExternalStorageDirectory().getAbsolutePath();
	
	private static final String SYMMALG = MagicXSign_Type.XSIGN_CRYPTO_ALG_ARIA;
	private static final int PKI_TYPE 	= MagicXSign_Type.XSIGN_PKI_TYPE_GPKI;
	private static final int CERT_TYPE 	= MagicXSign_Type.XSIGN_PKI_CERT_TYPE_USER;
	private static final int KEY_TYPE 	= MagicXSign_Type.XSIGN_PKI_CERT_SIGN;
	private static final int MEDIA_TYPE = MagicXSign_Type.XSIGN_PKI_MEDIA_TYPE_REMOVABLE;
	
	private static Cert cert = null;
	
	/**
	 * 인증서 로드 (스마트폰에 저장되어 있는 첫번째 인증서)
	 * @return 인증서 바이너리
	 */
	public static byte[] loadCert(Context context){
		MagicXSign Xsign = new MagicXSign();
		
		int[] nMediaType = new int[1];
		byte[] bnCert = null;
		try{
			Xsign.Init(context, MagicXSign_Type.XSIGN_DEBUG_LEVEL_1);
			Xsign.MEDIA_Load(PKI_TYPE, CERT_TYPE, KEY_TYPE, MEDIA_TYPE, SDCARD);
        	
        	if(Xsign.MEDIA_GetCertCount() <= 0)
        		throw new NotCertificateException();

        	bnCert = Xsign.MEDIA_ReadCert(INDEX , MagicXSign_Type.XSIGN_PKI_CERT_SIGN, nMediaType);
    	}
    	catch (Exception e) {
			Log.e("", "", e);
		}
    	finally{
    		try {
    			if(Xsign != null){
    				Xsign.MEDIA_UnLoad();
    				Xsign.Finish( );
    			}
			} 
    		catch (Exception e) {
				Log.e("", "", e);
			}
    	}
    	return bnCert;	
	}	
	
	/**
	 * 
	 * @param context
	 * @return 인증서 바이너리
	 */
	public static byte[] getSignData(Context context, String pass, byte[] signMsg){
		MagicXSign Xsign = new MagicXSign();
		byte[] signData = null;
		
		try{				
			Xsign.Init(context, MagicXSign_Type.XSIGN_DEBUG_LEVEL_1);
			Xsign.MEDIA_Load(PKI_TYPE, CERT_TYPE, KEY_TYPE, MEDIA_TYPE, SDCARD);
			
			signData = Xsign.CMS_SignData(MagicXSign_Type.XSIGN_PKI_OPT_USE_CONTENT_INFO, INDEX, pass, signMsg);
    	}
    	catch (Exception e) {
    		Log.e("", "", e);
		}
    	finally{
    		try {
    			if(Xsign != null){
    				Xsign.MEDIA_UnLoad();
    				Xsign.Finish( );
    			}
			} 
    		catch (Exception e) {
    			Log.e("", "", e);
			}
    	}
    	return signData;	
	}
	
	/**
	 * 인증서 정보 가져오기
	 * @param context
	 * @return 인증서 정보
	 */
	public static Cert getCertInfo(Context context){
		return getCertInfo(context, loadCert(context));
	}
	
	/**
	 * 인증서 정보 가져오기
	 * @param context
	 * @param binCert 인증서 바이너리
	 * @return 인증서 정보
	 */
	private static Cert getCertInfo(Context context, byte[] binCert){
		if(cert != null) return cert;	
		
		MagicXSign Xsign = new MagicXSign();
		try{
			cert = new Cert();	
			
			Xsign.Init(context, MagicXSign_Type.XSIGN_DEBUG_LEVEL_1);
			String OID = Xsign.CERT_GetAttribute(binCert, MagicXSign_Type.XSIGN_CERT_ATTR_POLICY_ID, true);

			// 사용 용도
			cert.setOID(ParsePolicy.Parse(OID));
			
			// 인증서 사용자 이름 설정
			cert.setUserName(Xsign.CERT_GetAttribute( null, MagicXSign_Type.XSIGN_CERT_ATTR_REAL_NAME, false ));
			
			// 인증서 유효기간 설정
			cert.setValidityTo(Xsign.CERT_GetAttribute( null, MagicXSign_Type.XSIGN_CERT_ATTR_EXPIRATION_TO, false ));
			cert.setValidityFrom(Xsign.CERT_GetAttribute( null, MagicXSign_Type.XSIGN_CERT_ATTR_EXPIRATION_FROM, false ));
			
			// 인증서 발급기관 설정
			String IssuerDN = Xsign.CERT_GetAttribute( null, MagicXSign_Type.XSIGN_CERT_ATTR_ISSUER_DN, false );
			String IssuerDN_O = ParsePolicy.GetDataFormString("o=", IssuerDN);
			cert.setIssuerDN_O(IssuerDN_O);
			String IssuerDN_CA = ParsePolicy.GetDataFormString("cn=", IssuerDN);
			if("CA131000002".equals(IssuerDN_CA)){
				IssuerDN_CA = "행정안전부";
			}
			cert.setIssuerDN_CA(IssuerDN_CA);
			return cert;
		}
		catch (Exception e) {
		}		
		return null;
	}
	
	/**
	 * 인증서 비밀번호 확인
	 * @param password 비밀번호
	 * @return boolean true:일치, false:불일치
	 */
	public static boolean checkPassword(Context context, String password){
		MagicXSign Xsign = new MagicXSign();
		
		try{
			Xsign.Init(context, MagicXSign_Type.XSIGN_DEBUG_LEVEL_1);
			Xsign.MEDIA_Load(PKI_TYPE, CERT_TYPE, KEY_TYPE, MEDIA_TYPE, SDCARD);
			
			if(Xsign.MEDIA_GetCertCount() <= 0){
        		throw new NotCertificateException();
			}      	
        	byte[] binKey = Xsign.MEDIA_ReadPriKey(INDEX, MagicXSign_Type.XSIGN_PKI_CERT_SIGN);
			Xsign.PRIKEY_Decrypt(binKey, password);
			
			return true;
    	}
    	catch (Exception e) {
			Log.e("", "", e);
		}
    	finally{
    		try {
    			if(Xsign != null){
    				Xsign.MEDIA_UnLoad();
    				Xsign.Finish( );
    			}
			} 
    		catch (Exception e) {
    			Log.e("", "", e);
			}
    	}
    	return false;
	}
	
	/**
	 * 인증서 비밀번호 변경
	 * @param context 
	 * @param oldPassword 기존 비밀번호
	 * @param newPassword 신규 비밀번호
	 * @return true or false
	 */
	public static boolean changePassword(Context context, String oldPassword, String newPassword){
		MagicXSign Xsign = new MagicXSign();		
		boolean flag = false;
		
		try{
			Xsign.Init(context, MagicXSign_Type.XSIGN_DEBUG_LEVEL_1);
			Xsign.MEDIA_Load(PKI_TYPE, CERT_TYPE, KEY_TYPE, MEDIA_TYPE, SDCARD);
			
			if(Xsign.MEDIA_GetCertCount() <= 0){
        		throw new NotCertificateException();
			}      	
        	flag = Xsign.MEDIA_ChangePassword(INDEX, oldPassword, newPassword);
    	}
    	catch (Exception e) {
    		Log.e("", "", e);	
		}
    	finally{
    		try {
    			if(Xsign != null){
    				Xsign.MEDIA_UnLoad();
    				Xsign.Finish( );
    			}
			} 
    		catch (Exception e) {
    			Log.e("", "", e);
			}
    	}
		return flag;
	}
	
	/**
	 * 인증서 삭제
	 * @param context
	 * @return true or false
	 */
	public static boolean deleteCertificate(Context context){
		MagicXSign Xsign = new MagicXSign();
		boolean flag = false;
		
		try{
			Xsign.Init(context, MagicXSign_Type.XSIGN_DEBUG_LEVEL_1);
			Xsign.MEDIA_Load(PKI_TYPE, CERT_TYPE, KEY_TYPE, MEDIA_TYPE, SDCARD);
			
			if(Xsign.MEDIA_GetCertCount() <= 0){
        		throw new NotCertificateException();
			} 	
        	flag = Xsign.MEDIA_DeleteCertificate(INDEX);
    	}
    	catch (Exception e) {
    		Log.e("", "", e);
		}
    	finally{
    		try {
    			if(Xsign != null){
    				Xsign.MEDIA_UnLoad();
    				Xsign.Finish( );
    			}
			} 
    		catch (Exception e) {
    			Log.e("", "", e);
			}
    	}    	
		return flag;
	}
	
	/**
	 * 서버에서 인증서 가져오기
	 * @param context
	 * @param handler
	 */
	public static void copyCert(Context context, Handler handler){
		handler.sendEmptyMessage(GET_AUTHCODE_START);
		
		MagicXSign	xSign = null;
	    MagicMRSPhone mXRSPhone = null;
	    
	    String phoneNo = "010000000";
		int			nResult = MagicMRSPhone.MAGICMRSPHONE_FAIL;
		byte[]		pSignCert = null, pSignPri = null, pKmCert = null, pKmPri = null;
        String		strAuthCode = null;
        
		try{
			xSign = new MagicXSign();
	        mXRSPhone = new MagicMRSPhone();
			
        	xSign.Init(context, MagicXSign_Type.XSIGN_DEBUG_LEVEL_3);
        	
        	// MagicMRSPhone을 초기화 한다.	        	
            nResult = mXRSPhone.MagicMRSPhone_Init(context);
            if(nResult != MagicMRSPhone.MAGICMRSPHONE_OK){
            	return;
            }
            
            // 인증서 이동 서버정보를 설정한다.	            
            nResult = mXRSPhone.MagicMRSPhone_SetHostInfo(SERVER_IP, SERVER_PORT);
            if(nResult != MagicMRSPhone.MAGICMRSPHONE_OK){
            	mXRSPhone.MagicMRSPhone_UnInit();            	
            	handler.sendEmptyMessage(SET_HOST_INFO_FAIL);
            	return;
            }
            
            // 사용자 정보를 설정한다.
            nResult = mXRSPhone.MagicMRSPhone_SetUserInfo(phoneNo, phoneNo, MagicMRSPhone.MAGICMRSPHONE_SERVICETYPE_IMPORT, SERVER_ID);	            
            if(nResult != MagicMRSPhone.MAGICMRSPHONE_OK){
            	mXRSPhone.MagicMRSPhone_UnInit();
            	handler.sendEmptyMessage(SET_USER_INFO_FAIL);
            	return;
            }
            
            // 인증번호를 가져온다.
            strAuthCode = mXRSPhone.MagicMRSPhone_GetAuthCode();
            if(strAuthCode == null){
            	nResult = mXRSPhone.MagicMRSPhone_GetErrCode();
            	handler.sendEmptyMessage(GET_AUTHCODE_FAIL);
            	return;
            }
           
            // 화면에 인증 번호 표시
            Message msg = handler.obtainMessage();		
    		msg.what = GET_AUTHCODE_OK;
    		msg.obj = strAuthCode;
    		handler.sendMessage(msg);
            
            // 인증서 용도를 가져온다.
	        int nCertUsage = mXRSPhone.MagicMRSPhone_GetCertUsage();
	        if(nCertUsage == MagicMRSPhone.MAGICMRSPHONE_FAIL){
	        	nResult = mXRSPhone.MagicMRSPhone_GetErrCode();
	        	handler.sendEmptyMessage(GET_USAGE_FAIL);
	        	return;
	        }
	        
	        // 서명용 인증서를 가져온다.
	        if(nCertUsage == MagicMRSPhone.MAGICMRSPHONE_USAGE_SIGN || nCertUsage == MagicMRSPhone.MAGICMRSPHONE_USAGE_ALL ){		        	
	        	pSignCert = mXRSPhone.MagicMRSPhone_GetSignCert();
	        	pSignPri = mXRSPhone.MagicMRSPhone_GetSignPri();

	        	try {
	        		xSign.MEDIA_Load( MagicXSign_Type.XSIGN_PKI_TYPE_NPKI, MagicXSign_Type.XSIGN_PKI_CERT_TYPE_USER, 
	        				MagicXSign_Type.XSIGN_PKI_CERT_SIGN, MagicXSign_Type.XSIGN_PKI_MEDIA_TYPE_REMOVABLE, SDCARD);
	        		xSign.MEDIA_WriteCertAndPriKey(pSignCert, pSignPri, MagicXSign_Type.XSIGN_PKI_MEDIA_TYPE_REMOVABLE);
	        		xSign.MEDIA_UnLoad();
	        		handler.sendEmptyMessage(COPY_CERT_OK);
				} 
	        	catch (Exception e){
	        		try {
	        			xSign.MEDIA_UnLoad();
					} 
					catch(MagicXSign_Exception e1){
						Log.e("", "", e);
					}
					handler.sendEmptyMessage(COPY_CERT_ERROR);
					Log.e("", "", e);
				}
	        }
	        
	        // 암호화용 인증서를 가져온다.
	        if(nCertUsage == MagicMRSPhone.MAGICMRSPHONE_USAGE_KM || nCertUsage == MagicMRSPhone.MAGICMRSPHONE_USAGE_ALL){
	        	pKmCert = mXRSPhone.MagicMRSPhone_GetKmCert();
	        	pKmPri = mXRSPhone.MagicMRSPhone_GetKmPri();

	        	try {
	        		xSign.MEDIA_Load( MagicXSign_Type.XSIGN_PKI_TYPE_NPKI, MagicXSign_Type.XSIGN_PKI_CERT_TYPE_USER, MagicXSign_Type.XSIGN_PKI_CERT_KM, MagicXSign_Type.XSIGN_PKI_MEDIA_TYPE_REMOVABLE, SDCARD);
	        		xSign.MEDIA_WriteCertAndPriKey( pKmCert, pKmPri, MagicXSign_Type.XSIGN_PKI_MEDIA_TYPE_REMOVABLE );
	        		xSign.MEDIA_UnLoad();
	        		handler.sendEmptyMessage(COPY_CERT_OK);
				}
	        	catch (Exception e) {
					try {
						xSign.MEDIA_UnLoad();
					} 
					catch (MagicXSign_Exception e1) {
						Log.e("", "", e);
					}
					handler.sendEmptyMessage(COPY_CERT_ERROR);
					Log.e("", "", e);
				}
	        }
	        handler.sendEmptyMessage(COPY_CERT_END);
        }
        catch (Exception e) {
        	Log.e("", "", e);
		}
        finally{
			try{
				if(xSign != null){
					xSign.Finish();
				}
				if(mXRSPhone != null){
					mXRSPhone.MagicMRSPhone_UnInit();
				}
			}
			catch (Exception e) {
				Log.e("", "", e);
			}
		}
	}
	
	/**
	 * 비대칭키 암호화
	 * @param plainText 암호화 대상
	 * @return byte[] 암호화된 바이너리
	 * @throws Exception
	 */
	public static byte[] encData(Context context, byte[] data) throws Exception {		
		MagicXSign	Xsign = new MagicXSign();
		int[]	nMediaType = new int[1];
    	byte[] 	binCert = null, binEncData = null;
    	
    	try{        	
    		Xsign.Init(context, MagicXSign_Type.XSIGN_DEBUG_LEVEL_1);
			Xsign.MEDIA_Load(MagicXSign_Type.XSIGN_PKI_TYPE_GPKI, MagicXSign_Type.XSIGN_PKI_CERT_TYPE_USER, MagicXSign_Type.XSIGN_PKI_CERT_KM, MagicXSign_Type.XSIGN_PKI_MEDIA_TYPE_ALL, SDCARD);
			
			if(Xsign.MEDIA_GetCertCount() <= 0){
        		throw new NotCertificateException();
			}
			
			binCert = Xsign.MEDIA_ReadCert(INDEX , MagicXSign_Type.XSIGN_PKI_CERT_KM, nMediaType);			
    		binEncData = Xsign.CMS_EncryptData(MagicXSign_Type.XSIGN_PKI_OPT_USE_CONTENT_INFO, SYMMALG, binCert, data);
    	}
    	catch( Exception e ){
    		Log.e("", "", e);
    	}
    	finally {
    		try {
    			if(Xsign != null){
    				Xsign.MEDIA_UnLoad();
    				Xsign.Finish( );
    			}
			} 
    		catch (Exception e) {
    			Log.e("", "", e);
			}
    	}    	
    	return binEncData;
	}

	/**
	 * 비대칭키 복호화
	 * @param encData 복호화 대상
	 * @param password 인증서 비밀번호 (암호화될 당시에 사용된 인증서의 비밀번호)
	 * @return byte[] 복호화된 바이너리
	 * @throws Exception
	 */
	public static byte[] decData(Context context, byte[] encData, String password) throws Exception {
		MagicXSign	Xsign = new MagicXSign();
		int[]	nMediaType = new int[1];
    	byte[] 	binCert = null, binKey = null, binDecData = null;
		
    	try{
        	Xsign.Init(context, MagicXSign_Type.XSIGN_DEBUG_LEVEL_1);
    		Xsign.MEDIA_Load(MagicXSign_Type.XSIGN_PKI_TYPE_GPKI, MagicXSign_Type.XSIGN_PKI_CERT_TYPE_USER, 
    				MagicXSign_Type.XSIGN_PKI_CERT_KM, MagicXSign_Type.XSIGN_PKI_MEDIA_TYPE_ALL, SDCARD);

    		if(Xsign.MEDIA_GetCertCount() <= 0)
        		throw new NotCertificateException();
    		
    		binCert = Xsign.MEDIA_ReadCert(INDEX , MagicXSign_Type.XSIGN_PKI_CERT_KM, nMediaType);
    		binKey = Xsign.MEDIA_ReadPriKey(INDEX, MagicXSign_Type.XSIGN_PKI_CERT_KM);
    		
    		binDecData = Xsign.CMS_DecryptData(binCert, binKey, password, encData);
    	}
    	catch(Exception e) {
    		Log.e("", "", e);
    	}
    	finally {
    		try {
    			if(Xsign != null){
    				Xsign.MEDIA_UnLoad();
    				Xsign.Finish( );
    			}
			} 
    		catch (Exception e) {
    			Log.e("", "", e);
			}
    	}    	
    	return binDecData;
	}
}
